<?php
// keywords.php - 返回关键词列表JSON（优先Redis原子弹出1个；否则按分类随机抽取MySQL最多1000条 is_read=0，标记为1并推入Redis后弹出1个）

declare(strict_types=1);
ini_set('display_errors', '1');
error_reporting(E_ALL);
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

date_default_timezone_set('Asia/Shanghai');

$configPath = __DIR__ . '/config.php';
if (!file_exists($configPath)) {
    http_response_code(500);
    echo json_encode(['code' => 1, 'msg' => '缺少配置文件config.php', 'data' => null], JSON_UNESCAPED_UNICODE);
    exit;
}
$config = require $configPath;

$keywords = [];
$redis = null; $client = null;

// 1) 优先从 Redis 列表 keyword_list 原子弹出 1 条
try {
    $redisConf = $config['redis'] ?? null;
    if ($redisConf) {
        if (class_exists('Redis')) {
            $redis = new Redis();
            $redis->connect($redisConf['host'], (int)$redisConf['port'], (float)$redisConf['timeout']);
            if (!empty($redisConf['password'])) { $redis->auth($redisConf['password']); }
            if (isset($redisConf['db'])) { $redis->select((int)$redisConf['db']); }
            $val = $redis->lPop('keyword_list');
            if (is_string($val)) {
                $val = trim($val);
                if ($val !== '') { $keywords = [$val]; }
            }
        } elseif (class_exists('Predis\Client')) {
            $client = new Predis\Client([
                'scheme' => 'tcp',
                'host' => $redisConf['host'],
                'port' => (int)$redisConf['port'],
                'database' => (int)($redisConf['db'] ?? 0),
                'timeout' => (float)($redisConf['timeout'] ?? 2.0),
            ]);
            if (!empty($redisConf['password'])) { $client->auth($redisConf['password']); }
            $val = $client->lpop('keyword_list');
            if (is_string($val)) {
                $val = trim($val);
                if ($val !== '') { $keywords = [$val]; }
            }
        }
    }
} catch (Throwable $e) {
    // 忽略Redis错误，回落到MySQL
}

// 2) 若 Redis 未取到值，则按分类随机抽取 MySQL: 最多 1000 条，并标记为 1，然后推入Redis并弹出1条返回
$pdo = null;
if (count($keywords) === 0) {
    $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s',
        $config['db_host'], (int)$config['db_port'], $config['db_name'], $config['db_charset']
    );
    try {
        $pdo = new PDO($dsn, $config['db_user'], $config['db_pass'], [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$config['db_charset']}"
        ]);

        // 统计各分类未读数量
        $sqlTypes = 'SELECT `type`, COUNT(*) AS c FROM `keyword` WHERE `keyword` <> "" AND `is_read` = 0 GROUP BY `type`';
        $rowsType = $pdo->query($sqlTypes)->fetchAll();
        if (!$rowsType || count($rowsType) === 0) {
            echo json_encode(['code' => 0, 'msg' => 'ok', 'data' => ['keywords' => []]], JSON_UNESCAPED_UNICODE);
            exit;
        }

        // 计算总数，按比例为各type分配配额
        $totalRemain = 0; foreach ($rowsType as $rt) { $totalRemain += (int)$rt['c']; }
        $targetTotal = 1000;
        // 打乱顺序以增加随机性
        shuffle($rowsType);

        $selected = [];
        $idsToMark = [];
        $remainingNeed = $targetTotal;
        $typesLeft = count($rowsType);

        foreach ($rowsType as $rt) {
            $type = (string)$rt['type'];
            $count = (int)$rt['c'];
            if ($count <= 0 || $remainingNeed <= 0) { continue; }
            // 比例配额（向上取整），同时不超过该分类可用数量和剩余需求
            $quota = (int)ceil($targetTotal * ($count / max(1, $totalRemain)));
            $quota = max(1, min($quota, $count, $remainingNeed));
            // 计算随机偏移
            $maxOffset = max(0, $count - $quota);
            // 使用更安全的随机偏移
            $offset = $maxOffset > 0 ? random_int(0, $maxOffset) : 0;

            // 按随机偏移抽取该分类的一段
            $sqlFetch = 'SELECT `id`, `keyword` FROM `keyword` WHERE `keyword` <> "" AND `is_read` = 0 AND `type` = :type ORDER BY `id` LIMIT :lim OFFSET :off';
            $stmtF = $pdo->prepare($sqlFetch);
            $stmtF->bindValue(':type', $type, PDO::PARAM_STR);
            $stmtF->bindValue(':lim', $quota, PDO::PARAM_INT);
            $stmtF->bindValue(':off', $offset, PDO::PARAM_INT);
            $stmtF->execute();
            $rows = $stmtF->fetchAll();

            foreach ($rows as $r) {
                $val = trim((string)$r['keyword']);
                if ($val === '') { continue; }
                $selected[] = $val;
                $idsToMark[] = (int)$r['id'];
            }

            $remainingNeed -= count($rows);
            $typesLeft--;
            if ($remainingNeed <= 0) { break; }
        }

        if (count($selected) > 0) {
            // 标记为已读
            try {
                foreach (array_chunk($idsToMark, 200) as $chunk) {
                    $placeholders = rtrim(str_repeat('?,', count($chunk)), ',');
                    $sqlUpdate = "UPDATE `keyword` SET `is_read` = 1, `update_time` = CURRENT_TIMESTAMP WHERE `id` IN ($placeholders)";
                    $stmtU = $pdo->prepare($sqlUpdate);
                    $stmtU->execute($chunk);
                }
            } catch (Throwable $e) {
                error_log('Failed to mark is_read: ' . $e->getMessage());
            }

            // 推入Redis并弹出1个返回
            try {
                $redisClient = $redis ?? $client;
                if ($redisClient) {
                    if ($redisClient instanceof Redis) {
                        foreach (array_chunk($selected, 500) as $chunk) {
                            $args = array_merge(['keyword_list'], $chunk);
                            call_user_func_array([$redisClient, 'rPush'], $args);
                        }
                        $val = $redisClient->lPop('keyword_list');
                    } else {
                        foreach (array_chunk($selected, 500) as $chunk) {
                            foreach ($chunk as $v) { $redisClient->rpush('keyword_list', [$v]); }
                        }
                        $val = $redisClient->lpop('keyword_list');
                    }
                    if (is_string($val)) {
                        $val = trim($val);
                        if ($val !== '') { $keywords = [$val]; }
                    } else {
                        // Redis回退：直接返回第一条
                        $keywords = [reset($selected)];
                    }
                } else {
                    // 无Redis：直接返回第一条
                    $keywords = [reset($selected)];
                }
            } catch (Throwable $e) {
                error_log('Failed to push/pop Redis: ' . $e->getMessage());
                $keywords = [reset($selected)];
            }
        }

    } catch (Throwable $e) {
        http_response_code(500);
        echo json_encode(['code' => 1, 'msg' => '数据库错误: ' . $e->getMessage(), 'data' => null], JSON_UNESCAPED_UNICODE);
        exit;
    }
}

echo json_encode(['code' => 0, 'msg' => 'ok', 'data' => ['keywords' => $keywords]], JSON_UNESCAPED_UNICODE); 